using System;
using System.IO;
using System.Runtime.InteropServices;
using Newtonsoft.Json;
using Newtonsoft.Json.Linq;
using SG.Productivity.DesktopControlPanel.Properties;
using SG.Productivity.DesktopControlPanel.Support;

namespace SG.Productivity.DesktopControlPanel.ParallelQuery
{
    [Guid("ADCD8990-C67C-4A0D-AD51-226F01B20382")]
    public struct QueryIdentifier : IEquatable<QueryIdentifier>
    {
        private const string QueryParam = "q";
        private const string UserParam = "user";
        private const string ProviderParam = "acceptedProviders";

        private readonly string _query;
        private readonly string _user;
        private readonly Uri _provider;
        private readonly int _hashCode;

        public override int GetHashCode()
        {
            return _hashCode;
        }

        public bool Equals(QueryIdentifier other)
        {
            return _hashCode == other.GetHashCode() && _query == other.Query && _user == other.User &&
                   Provider == other.Provider;
        }

        public override bool Equals(object obj)
        {
            if (_hashCode != obj.GetHashCode())
                return false;

            return obj is QueryIdentifier && Equals((QueryIdentifier) obj);
        }

        public QueryIdentifier(string query, string user, Uri provider)
        {
            _query = string.Intern(query);
            _user = string.Intern(user);
            _provider = provider;

            unchecked
            {
                _hashCode = 2147483647;
                _hashCode = _hashCode*1299721 ^ _query.GetHashCode();
                _hashCode = _hashCode*1299721 ^ _user.GetHashCode();
                _hashCode = _hashCode*1299721 ^ _provider.GetHashCode();
            }
        }

        public string Query { get { return _query; } }

        public string User { get { return _user; } }

        public Uri Provider { get { return _provider; } }

        public static implicit operator QueryIdentifier(JObject jsonObject)
        {
            var q = jsonObject[QueryParam].ToObject<string>();
            var user = jsonObject[UserParam].ToObject<string>();

            var providerList = jsonObject[ProviderParam];

            if (providerList == null)
                throw new ArgumentException(Resources.QueryIdentifier_op_Implicit_The_JSON_does_not_contain_an_acceptedProviders_attribute_, "jsonObject");

            var provider = providerList.Type == JTokenType.Array ? providerList.First : providerList;

            return new QueryIdentifier(q, user, provider.ToObject<Uri>());
        }

        public static implicit operator StrongNameValueCollection(QueryIdentifier @this)
        {
            return new StrongNameValueCollection
                {
                    {QueryParam, @this.Query},
                    {UserParam, @this.User},
                    {ProviderParam, @this.Provider.ToString()}
                };
        }
    }
}
